﻿using System;

namespace WSKnowledge.WSHelpers.Rules
{
	public interface IRule
	{
		bool Validate(object obj);
	}

	public class RuleException : Exception
	{
		public RuleException(string message) : base(message) { }
	}

	public class StringRule : IRule
	{
		protected bool _canAcceptNull;
		protected bool _canAcceptEmpty;
		protected bool _onlyExactLength;
		protected int _maxLength = Int32.MaxValue;

		public int MaxLength { get { return _maxLength; } }

		public bool CanAcceptNull { get { return _canAcceptNull; } }

		public bool CanAcceptEmpty { get { return _canAcceptEmpty; } }

		public bool OnlyExactLength { get { return _onlyExactLength; } }

		protected StringRule() { }

		public bool Validate(object obj)
		{
			if (!(obj is string)) return false;

			string input = (string)obj;

			if (!_canAcceptNull && input.IsNull())
				return false;

			if (!_canAcceptEmpty && input.IsEmpty())
				return false;

			if (input.IsGreaterThan(_maxLength))
				return false;

			if (_onlyExactLength && input.IsLessThan(_maxLength))
				return false;

			return true;
		}

		#region Fluent Interface to define string rules

		public static StringRule CreateRule()
		{
			return new StringRule();
		}

		public StringRule WithLengthOf(int maxLength)
		{
			_maxLength = maxLength;
			return this;
		}

		public StringRule AcceptsNull()
		{
			_canAcceptNull = true;
			return this;
		}

		public StringRule AcceptsEmpty()
		{
			if (OnlyExactLength)
				throw new RuleException("Can not accept only exact length and empty at the same time");

			_canAcceptEmpty = true;
			return this;
		}

		public StringRule AcceptsExactLengthOnly()
		{
			if (CanAcceptEmpty)
				throw new RuleException("Can not accept only exact length and empty at the same time");

			_onlyExactLength = true;
			return this;
		}

		#endregion
	}

	public class ExampleOfStringRule
	{
		public ExampleOfStringRule()
		{
			// you can apply different methods in any order.
			// this is for a 20 characters length that accepts null and but not empty!
			var newRule1 = StringRule.CreateRule()
				.WithLengthOf(20)
				.AcceptsNull();

			// this is for a 50 characters length that does not accept null or empty!
			var newRule2 = StringRule.CreateRule()
				.WithLengthOf(50);

			// this is for a 30 characters length EXACTLY and accepts NULL and EMPTY!!
			// this must throw an exception because it is not logic
			var newRule3 = StringRule.CreateRule()
				.WithLengthOf(30)
				.AcceptsExactLengthOnly()
				.AcceptsNull()
				.AcceptsEmpty();
		}
	}
}
